package br.com.gesclub.business.dao.core;

import br.com.gesclub.business.dao.GenericDao;
import br.com.gesclub.business.model.BaseEntity;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Criterion;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

/**
 * Implementação do DaoGenérico possuindo as implementações mais comuns encontradas em
 * todos os outros DAO's da aplicação.
 *
 * @param <T>  - Tipo da objeto que será persistido no banco de dados.
 * @param <ID> - Id que será passado para consultas
 */
public abstract class GenericDaoImpl<T extends BaseEntity, ID extends Serializable>
        extends HibernateDaoSupport
        implements GenericDao<T, ID> {

    private Class<T> persistentClass;

    /**
     * Recupera a instancia da classe que está sendo persistida.
     *
     * @return Class<T>
     */
    public Class<T> getPersistentClass() {
        return persistentClass;
    }

    /**
     * Construtor do GenericDao que recupera, em tempo de execução,
     * a classe que está sendo persistida pelo hibernate.
     */
    public GenericDaoImpl() {
        persistentClass = (Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass())
                .getActualTypeArguments()[0];
    }

    @Override
    public ID save(final T object) {
        try {
            return (ID) getHibernateTemplate().save(object);
        } catch (HibernateException he) {
            throw convertHibernateAccessException(he);
        }
    }

    @Override
    public void saveOrUpdate(final T object) {
        try {
            getHibernateTemplate().saveOrUpdate(object);
        } catch (HibernateException he) {
            throw convertHibernateAccessException(he);
        }

    }

    @Override
    public T update(final T object) {
        try {
            return (T) getHibernateTemplate().merge(object);
        } catch (HibernateException he) {
            throw convertHibernateAccessException(he);
        }
    }

    @Override
    public void delete(final T object) {
        try {
            getHibernateTemplate().delete(object);
        } catch (HibernateException he) {
            throw convertHibernateAccessException(he);
        }
    }

    @Override
    public T findById(final ID id) {
        try {
            return (T) getHibernateTemplate().get(getPersistentClass(), id);
        } catch (HibernateException he) {
            throw convertHibernateAccessException(he);
        }
    }

    @Override
    public List<T> findAll() {
        try {
            return getHibernateTemplate().loadAll(getPersistentClass());
        } catch (HibernateException he) {
            throw convertHibernateAccessException(he);
        }
    }

    @Override
    public List<T> findByCriteria(final Criterion... criterion) {
        Criteria criteria = getHibernateTemplate()
                .getSessionFactory()
                .getCurrentSession()
                .createCriteria(getPersistentClass());

        for (Criterion c : criterion) {
            criteria.add(c);
        }

        List<T> result = null;
        try {
            result = new ArrayList<T>(criteria.list());
        } catch (HibernateException he) {
            throw convertHibernateAccessException(he);
        }

        return result != null ? result : new ArrayList<T>();
    }

}
